﻿#include <QDebug>

extern "C" {
#include <libswresample/swresample.h>
}

#include "AVAudioConvert.h"

AVAudioConvert::AVAudioConvert(int inSample, int inChannel, int inFormat, int outSample, int outChannel, int outFormat)
{
    m_outSampleRate = outSample;
    m_outChannels = outChannel;
    m_outFormat = outFormat;
    m_aud_convert_ctx = swr_alloc_set_opts(NULL,
                                           av_get_default_channel_layout(outChannel),
                                           (AVSampleFormat)outFormat,
                                           outSample,
                                           av_get_default_channel_layout(inChannel),
                                           (AVSampleFormat)inFormat,
                                           inSample,
                                           0, NULL);
    if (swr_init(m_aud_convert_ctx) != 0) {
        swr_free(&m_aud_convert_ctx);
    }
}

int AVAudioConvert::audio_rescale_private(int srcSampleRate,int srcChannels,int srcFormat,int srcNbSamples,uint8_t **srcData, int dstSampleRate, int dstChannels, enum AVSampleFormat dstSampleFormat,  uint8_t *outBuf, int *outBufSize, int *outNbSamples,int *linesize)
{
    /* 计算重采样后的样本点个数，由样本点时间相同，推出方程：X/8000 = nb/44100 */
    int32_t maxNbSample = av_rescale_rnd(srcNbSamples, dstSampleRate, srcSampleRate, AV_ROUND_UP);

    /* 采用静态分配，故这里不需要释放 */
    if (av_samples_alloc_array_and_samples(&dstBuffer,linesize,dstChannels,
                                           maxNbSample,(AVSampleFormat)dstSampleFormat,1) < 0){
        printf("alloc fill error");
        return -1;
    }

    int dstNBSamples = av_rescale_rnd(swr_get_delay(m_aud_convert_ctx,srcSampleRate) + srcNbSamples,
                                      dstSampleRate,srcSampleRate,AV_ROUND_UP);

    if (dstNBSamples > maxNbSample) {
        av_freep(&dstBuffer[0]);
        int ret = av_samples_alloc(dstBuffer,linesize,dstChannels,dstNBSamples,dstSampleFormat,1);
        if (ret < 0) {
            return -1;
        }
    }

    /* swr_convert是执行重采样后的样本点个数，这个值可能小于理论样本点个数（绝大部分等于理论样本点），也有可能为0。
     * swr_convert返回0时，数据就可以丢弃了，尤其在44100转8000，高转低发生。
    **/
    int ret = -1;
    if ((ret = swr_convert(m_aud_convert_ctx,
                           dstBuffer, dstNBSamples,
                           (const uint8_t**)srcData, srcNbSamples)) < 0) {
        printf("Could not convert input samples\n");
        return -1;
    }

    *outNbSamples = ret;

    /* 由于内存对齐的缘故，av_samples_alloc的返回值，不是真实数据长度(含有内存对其的数据），故需要通过这个来返回最终字节大小*/
    int testLine[8] = {0};
    int realSize = av_samples_get_buffer_size(testLine,dstChannels,ret,(AVSampleFormat)dstSampleFormat,1);
    if (realSize < 0) {
        return -1;
    }

    *outBufSize = realSize;

    /* 这里若是非平面转为平面，outBuf的数据格式从概念上是不对的，虽然机器可能不管这些 */
    memcpy(outBuf,dstBuffer[0],realSize);

    if(dstBuffer){
        av_freep(&dstBuffer[0]);
    }
    av_freep(&dstBuffer);

    return 0;
}

int AVAudioConvert::audio_rescale(AVFrame *srcFrame,uint8_t *outBuf, int *outBufSize, int *outNbSamples)
{
    return audio_rescale_private(srcFrame->sample_rate,srcFrame->channels,srcFrame->format,srcFrame->nb_samples,srcFrame->data,m_outSampleRate,m_outChannels,(AVSampleFormat)m_outFormat,outBuf,outBufSize,outNbSamples,NULL);
}

AVAudioConvert::~AVAudioConvert()
{
    swr_free(&m_aud_convert_ctx);
}
